package lib

import (
	"bufio"
	"db/mysql"
	"encoding/gob"
	"encoding/json"
	"fmt"
	"github.com/pkg/errors"
	"github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/disk"
	"github.com/shirou/gopsutil/host"
	"github.com/shirou/gopsutil/load"
	"github.com/shirou/gopsutil/mem"
	wnet "github.com/shirou/gopsutil/net"
	"io"
	"logs"
	"net"
	"os"
	"strconv"
	"strings"
	"sync"
)

// 混合类型的struct
type ComplexData struct {
	NowTime     string
	Lan         string
	Ip          string
	Host        *host.InfoStat
	DiskInfo    []disk.PartitionStat
	DiskUsed    []*disk.UsageStat
	CpuLoad     *load.AvgStat
	Memory      *mem.VirtualMemoryStat
	Flow        []wnet.IOCountersStat
	NetWorkInfo []wnet.InterfaceStat
	Process     int
	CpuInfo     []cpu.InfoStat
	CpuUsed     []float64
}

var (
	Port        = ":61000"      // 服务端接受的端口
	Api         = ":9090"       //api端口
	SaveMaxData = 144           //保存数据长度
	Addr        = "localhost"   //服务端IP地址
	MonDir      = []string{"/"} //监控磁盘分区
	Debug       = false         //是否在日志中显示详细数据
	Speed       = 300           //客户端执行频率,300s/次
	LanName     = "Default"     //数据归属LanName

	DBStat       = false
	DB           []string
	SaveDatasMap = map[string][]ComplexData{}
)

/**
net.Conn 实现了io.Reader  io.Writer  io.Closer接口
Open 返回一个有超时的TCP链接缓冲readwrite
*/
func Open(addr string) (*bufio.ReadWriter, error) {
	// Dial the remote process.
	// Note that the local port is chosen on the fly. If the local port
	// must be a specific one, use DialTCP() instead.
	logs.PrintlnFile("Dial "+addr, 3)
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return nil, errors.Wrap(err, "Dialing "+addr+" failed")
	}
	return bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)), nil
}

type HandleFunc func(*bufio.ReadWriter)

type EndPoint struct {
	listener net.Listener
	// handlefunc是一个处理传入命令的函数类型。 它接收打包在一个读写器界面中的开放连接。
	handler map[string]HandleFunc

	// map不是线程安全的，所以需要读写锁控制
	m sync.RWMutex
}

func NewEndPoint() *EndPoint {
	return &EndPoint{
		handler: map[string]HandleFunc{},
	}
}

// 添加数据类型处理方法
func (e *EndPoint) AddHandleFunc(name string, f HandleFunc) {
	e.m.Lock()
	e.handler[name] = f
	e.m.Unlock()
}

// 验证请求数据类型，并发送到对应处理函数
func (e *EndPoint) handleMessage(conn net.Conn) {
	rw := bufio.NewReadWriter(bufio.NewReader(conn),
		bufio.NewWriter(conn))
	defer conn.Close()
	for {
		cmd, err := rw.ReadString('\n')
		switch {
		case err == io.EOF:
			//logs.PrintlnFile("接收完成.", 3)
			return
		case err != nil:
			//logs.PrintlnFile("接收出错", 5)
			return
		}

		cmd = strings.Trim(cmd, "\n ")
		e.m.RLock()
		handleCmd, ok := e.handler[cmd]
		if !ok {
			logs.PrintlnFile("未注册的请求数据类型.", 4)
			return
		}
		//具体处理链接数据
		handleCmd(rw)
	}
}

func (e *EndPoint) Listen() error {
	var err error
	e.listener, err = net.Listen("tcp", Port)
	if err != nil {
		return errors.Wrap(err, "TCP服务无法监听在端口"+Port)
	}
	logs.PrintlnFile("服务监听成功 : "+e.listener.Addr().String(), 3)
	for {
		conn, err := e.listener.Accept()
		if err != nil {
			logs.PrintlnFile("请求监听失败!", 5)
			continue
		}
		// 开始处理新链接数据
		go e.handleMessage(conn)
	}

}

func HandleStrings(rw *bufio.ReadWriter) {
	s, err := rw.ReadString('\n')
	if err != nil {
		logs.PrintlnFile("链接无法读取.", 5)
		return
	}

	s = strings.Trim(s, "\n ")
	// ....
	_, err = rw.WriteString("处理完成......\n")
	if err != nil {
		logs.PrintlnFile("链接写入响应失败", 5)
		return
	}
	// 写入底层网络链接
	err = rw.Flush()
	if err != nil {
		logs.PrintlnFile("Flush写入失败", 5)
		return
	}
}

func HandleGob(rw *bufio.ReadWriter) {
	var data ComplexData

	dec := gob.NewDecoder(rw)
	err := dec.Decode(&data)
	if err != nil {
		logs.PrintlnFile("无法解析的二进制数据.", 5)
		return
	}
	if Debug {
		logs.PrintlnFile("Receive Data : "+fmt.Sprint(data), 3)
	} else {
		logs.PrintlnFile("Receive Data : Success", 3)
	}

	// 数据处理
	filteringData(data)
}

//server or client
func GetConfigInput(runtype string) {
	if runtype == "server" {
		Map := map[string]string{}
		end := len(os.Args) - 1
		for _, k := range os.Args[1:end] {
			array := strings.Split(k, "=")
			Map[array[0]] = array[1]
		}
		if i, ok := Map["-SetDB"]; ok {
			con_db := strings.Split(i, ":")
			st := mysql.Check(con_db)
			if st {
				DB = con_db
				DBStat = true
				logs.PrintlnFile("The database '"+DB[2]+":"+DB[3]+"/"+DB[4]+"' connection is successful and the collected data will be written to the database.", 3)
			} else {
				DBStat = false
				logs.PrintlnFile("Failure of database connection,Data cannot be written.", 5)
			}
		}
		if i, ok := Map["-SerPort"]; ok {
			Port = ":" + i
		}
		if i, ok := Map["-ApiPort"]; ok {
			Api = ":" + i
		}
		if i, ok := Map["-MaxData"]; ok {
			int_maxdata, _ := strconv.Atoi(i)
			SaveMaxData = int_maxdata
		}
		if i, ok := Map["-Debug"]; ok {
			rei, _ := strconv.Atoi(i)
			if rei >= 1 {
				Debug = true
			}
		}
		logs.PrintlnFile("加载服务端启动参数 => SerPort"+Port+" ApiPort"+Api+" MaxData:"+fmt.Sprint(SaveMaxData)+" Debug:"+fmt.Sprint(Debug), 3)
	} else if runtype == "client" {
		Map := map[string]string{}
		end := len(os.Args) - 1
		for _, k := range os.Args[1:end] {
			array := strings.Split(k, "=")
			Map[array[0]] = array[1]
		}
		if i, ok := Map["-LanName"]; ok {
			LanName = i
		}
		if i, ok := Map["-SerPort"]; ok {
			Port = ":" + i
		}
		if i, ok := Map["-SerAddr"]; ok {
			Addr = i
		}
		if i, ok := Map["-MonDir"]; ok {
			array := strings.Split(i, ",")
			MonDir = array
		}
		if i, ok := Map["-Debug"]; ok {
			rei, _ := strconv.Atoi(i)
			if rei >= 1 {
				Debug = true
			}
		}
		if i, ok := Map["-Speed"]; ok {
			rei, _ := strconv.Atoi(i)
			if rei < 10 {
				rei = 10
			}
			Speed = rei
		}
		logs.PrintlnFile("加载客户端启动参数 => SerPort"+Port+" SerAddr:"+Addr+" MonDir:"+fmt.Sprint(MonDir)+" Speed:"+fmt.Sprint(Speed)+" Debug:"+fmt.Sprint(Debug), 3)
	}
}

func filteringData(data ComplexData) {
	// json to map

	//保留数据到内存
	if len(SaveDatasMap[data.Lan]) >= SaveMaxData {
		SaveDatasMap[data.Lan] = append(SaveDatasMap[data.Lan][:0], SaveDatasMap[data.Lan][1:]...)
	}
	SaveDatasMap[data.Lan] = append(SaveDatasMap[data.Lan], data)
	//fmt.Println(len(SaveDatasMap[data.Lan]))

	// allmsg
	datahead := map[string]string{
		"NowTime": data.NowTime,
		"Lan":     data.Lan,
		"Ip":      data.Ip,
	}

	// NetWorkInfo
	var _NetWorkInfo []interface{}
	for _, k := range data.NetWorkInfo {
		NetWork := json.NewDecoder(strings.NewReader(fmt.Sprint(k)))
		NetWork.UseNumber()
		var x interface{}
		NetWork.Decode(&x)
		_NetWorkInfo = append(_NetWorkInfo, x)
	}
	//fmt.Println(_NetWorkInfo, "\n")

	// CpuUsed
	var _CpuUsed []interface{}
	for _, k := range data.CpuUsed {
		CpuUsed := json.NewDecoder(strings.NewReader(fmt.Sprint(k)))
		CpuUsed.UseNumber()
		var x interface{}
		CpuUsed.Decode(&x)
		_CpuUsed = append(_CpuUsed, x)
	}
	//fmt.Println(_CpuUsed, "\n")

	// CpuInfo
	var _CpuInfo []interface{}
	for _, k := range data.CpuInfo {
		CpuInfo := json.NewDecoder(strings.NewReader(fmt.Sprint(k)))
		CpuInfo.UseNumber()
		var x interface{}
		CpuInfo.Decode(&x)
		_CpuInfo = append(_CpuInfo, x)
	}
	//fmt.Println(_CpuInfo, "\n")

	// DiskInfo
	var _DiskInfo []interface{}
	for _, k := range data.DiskInfo {
		DiskInfo := json.NewDecoder(strings.NewReader(fmt.Sprint(k)))
		DiskInfo.UseNumber()
		var x interface{}
		DiskInfo.Decode(&x)
		_DiskInfo = append(_DiskInfo, x)
	}
	//fmt.Println(_DiskInfo, "\n")

	// Flow
	var _Flow []interface{}
	for _, k := range data.Flow {
		Flow := json.NewDecoder(strings.NewReader(fmt.Sprint(k)))
		Flow.UseNumber()
		var x interface{}
		Flow.Decode(&x)
		_Flow = append(_Flow, x)
	}
	//fmt.Println(_Flow, "\n")

	// DiskUsed
	var _DiskUsed []interface{}
	for _, k := range data.DiskUsed {
		DiskUsed := json.NewDecoder(strings.NewReader(fmt.Sprint(k)))
		DiskUsed.UseNumber()
		var x interface{}
		DiskUsed.Decode(&x)
		_DiskUsed = append(_DiskUsed, x)
	}
	//fmt.Println(_DiskUsed, "\n")

	// Host
	_Host := json.NewDecoder(strings.NewReader(fmt.Sprint(data.Host)))
	_Host.UseNumber()
	var host interface{}
	_Host.Decode(&host)
	//fmt.Println(host, "\n")

	// CpuLoad
	_CpuLoad := json.NewDecoder(strings.NewReader(fmt.Sprint(data.CpuLoad)))
	_CpuLoad.UseNumber()
	var cpuLoad interface{}
	_CpuLoad.Decode(&cpuLoad)
	//fmt.Println(cpuLoad, "\n")

	// Memory
	_Memory := json.NewDecoder(strings.NewReader(fmt.Sprint(data.Memory)))
	_Memory.UseNumber()
	var memory interface{}
	_Memory.Decode(&memory)
	//fmt.Println(memory, "\n")
	//rememory := memory.(interface{}).(map[string]interface{})
	//fmt.Println(rememory, "\n")

	// Process
	//fmt.Println(data.Process)

	// 写入数据库
	if DBStat {
		mysql.Start("Host", datahead, host, DB)
		mysql.Start("CpuLoad", datahead, cpuLoad, DB)
		mysql.Start("Memory", datahead, memory, DB)
		mysql.Start("Process", datahead, data.Process, DB)
		mysql.Start("DiskUsed", datahead, _DiskUsed, DB)
		mysql.Start("Flow", datahead, _Flow, DB)
		mysql.Start("DiskInfo", datahead, _DiskInfo, DB)
		mysql.Start("CpuInfo", datahead, _CpuInfo, DB)
		mysql.Start("CpuUsed", datahead, _CpuUsed, DB)
		mysql.Start("NetWorkInfo", datahead, _NetWorkInfo, DB)
	}
}

func typeof(v interface{}) string {
	return fmt.Sprintf("%T", v)
}
